/*******************************************
Module: zt_http.c
E-Mail: 24zhutian@gmail.com
Notices: Copyright (c) 2007-2011 ls
*******************************************/
#include "zt_config.h"
#include "zt_str.h"
#include "zt_http.h"

#ifdef _MSC_VER
#pragma comment(lib,"wininet.lib")

#ifndef INTERNET_OPTION_MAX_CONNS_PER_SERVER
#define INTERNET_OPTION_MAX_CONNS_PER_SERVER 73
#endif

#endif

ZT_API u_char* zt_http_sync_request_user_agent(u_char *useragent)
{
#ifdef _MSC_VER
	OSVERSIONINFO os;
	HKEY hKey;
	u_char psz[255];
	DWORD dwLen=255;
	
	os.dwOSVersionInfoSize=sizeof(os);
	GetVersionEx(&os);
	
	zt_strcpy(useragent,(u_char *)"Mozilla/4.0 (compatible; MSIE 6.0; Windows NT 5.1;)");
	if(RegOpenKeyEx(HKEY_LOCAL_MACHINE,"SOFTWARE\\Microsoft\\Internet Explorer",0,KEY_QUERY_VALUE,&hKey)==ERROR_SUCCESS){
		if(RegQueryValueEx(hKey,"Version",NULL,NULL,(PBYTE)psz,&dwLen)==ERROR_SUCCESS){
			wsprintf((char *)useragent,"Mozilla/4.0 (compatible; MSIE %.3s; Windows NT %d.%d;)",
				psz,os.dwMajorVersion,os.dwMinorVersion);
		}
		RegCloseKey(hKey);
	}
	return(useragent);
#endif
}

ZT_API HttpSyncRequest* zt_http_sync_request_new(const u_char *useragent,zt_uint_t timeout)
{
	HttpSyncRequest *req=NULL;
	unsigned long dwLen=0;
	
	req=ZT_MEM_MALLOC(HttpSyncRequest,1);
	if(req){		
		req->obj=NULL;
		req->conn=NULL;
		req->req=NULL;

		req->auto_redirect=0;
		req->timeout=timeout>0?timeout:30*1000;
		req->StatusCode=0;
		req->ContentLength=0;

		req->err=ZT_OK;
		req->maxconn=ZT_MAX_HTTP_CONNECT;

		req->hdr=NULL;
		req->response=NULL;

		req->host=NULL;
		req->query=NULL;

#ifdef _MSC_VER
		req->obj=InternetOpen((LPTSTR)useragent,INTERNET_OPEN_TYPE_DIRECT,NULL,NULL,0);
		if(req->obj){
			InternetSetOption(NULL,INTERNET_OPTION_MAX_CONNS_PER_SERVER,&req->maxconn,sizeof(req->maxconn));
			InternetSetOption(req->obj,INTERNET_OPTION_CONNECT_TIMEOUT,(LPVOID)&req->timeout,dwLen);
			InternetSetOption(req->obj,INTERNET_OPTION_RECEIVE_TIMEOUT,(LPVOID)&req->timeout,dwLen);
			InternetSetOption(req->obj,INTERNET_OPTION_SEND_TIMEOUT,(LPVOID)&req->timeout,dwLen);
		}
		else{
			free(req);
			req=NULL;
		}
#endif
	}
	return(req);
}

ZT_API zt_int_t	zt_http_sync_request_open(HttpSyncRequest *req,const u_char *method,const u_char *uri)
{
	URL_COMPONENTS uc;
	//u_char *HostName=NULL;
	//u_char *FileName=NULL;
	size_t size=0;
	DWORD flags;
	u_char szAccept[] = "*/*"; //we accept everything...
	LPTSTR AcceptTypes[2]={0};

	if(uri==NULL){
		return(0);
	}	
	if(req->req!=NULL){
		InternetCloseHandle(req->req);
		req->req=NULL;
	}
	//post the data...
	if (req->conn!=NULL){
		InternetCloseHandle(req->conn);
		req->conn=NULL;
	}	
	
	//let's split the url...
	uc.dwStructSize = sizeof(uc);
	uc.lpszScheme = NULL;
	uc.dwSchemeLength = 0;
	uc.lpszHostName = NULL;
	uc.dwHostNameLength = 1;
	uc.nPort = 0;
	uc.lpszUserName = NULL;
	uc.dwUserNameLength = 0;
	uc.lpszPassword = NULL;
	uc.dwPasswordLength = 0;
	uc.lpszUrlPath = NULL;
	uc.dwUrlPathLength = 1;
	uc.lpszExtraInfo = NULL;
	uc.dwExtraInfoLength = 0;

	if(InternetCrackUrl((LPCSTR)uri, size, 0, &uc)){
		__try{
			if(uc.dwHostNameLength>req->host->siz){
				__leave;
			}
			if(uc.dwUrlPathLength>req->query->siz){
				__leave;
			}
			ZT_MEMCPY(req->host->str,uc.lpszHostName,uc.dwHostNameLength);
			req->host->use=(size_t)uc.dwHostNameLength;
			ZT_STR_END(req->host);
			ZT_MEMCPY(req->query->str,uc.lpszUrlPath,uc.dwUrlPathLength);
			req->query->use=(size_t)uc.dwUrlPathLength;
			ZT_STR_END(req->query);
			
			if (uc.nPort == 443){
				//we are talking secure https
				flags = INTERNET_FLAG_NO_CACHE_WRITE | INTERNET_FLAG_SECURE |
					INTERNET_FLAG_IGNORE_CERT_CN_INVALID | INTERNET_FLAG_IGNORE_CERT_DATE_INVALID;
				if(!req->auto_redirect){
					flags |=INTERNET_FLAG_NO_AUTO_REDIRECT;
				}
			}
			else{
				//we are talking plain http
				//flags = INTERNET_FLAG_NO_CACHE_WRITE;//INTERNET_FLAG_NO_AUTO_REDIRECT;
				if(req->auto_redirect){
					flags = INTERNET_FLAG_NO_CACHE_WRITE;
				}
				else{
					flags = INTERNET_FLAG_NO_CACHE_WRITE|INTERNET_FLAG_NO_AUTO_REDIRECT;
				}
			}
			AcceptTypes[0]=(LPTSTR)szAccept;
			if(req->obj){
				req->conn = InternetConnect(req->obj,(LPTSTR)req->host->str, uc.nPort, NULL,NULL, INTERNET_SERVICE_HTTP, INTERNET_FLAG_NO_CACHE_WRITE, 0);
				if(req->conn){
					req->req = HttpOpenRequest(req->conn,(LPCSTR)method,(LPTSTR)req->query->str, NULL,NULL, (LPCTSTR*)AcceptTypes, flags, INTERNET_FLAG_NO_AUTO_REDIRECT);
				}
			}
			__leave;
		}
		__except(EXCEPTION_EXECUTE_HANDLER){
			if(req->req){
				InternetCloseHandle(req->req);
				req->req=NULL;
			}
		}		
	}
	if(!req->req){
		req->err=GetLastError();
		return(ZT_ERR);
	}

	return(ZT_OK);
}

ZT_API zt_int_t	zt_http_sync_request_header(HttpSyncRequest *req,const u_char *str)
{
	if(str&&req->req){
		HttpAddRequestHeaders(req->req,(LPTSTR)str,(DWORD)-1,HTTP_ADDREQ_FLAG_ADD|HTTP_ADDREQ_FLAG_REPLACE);
		return(ZT_OK);
	}
	return(ZT_ERR);
}

ZT_API zt_int_t	zt_http_sync_request_send(HttpSyncRequest *req,const u_char *data,size_t siz)
{
	DWORD d = 1, d2 = 0,dwlength=sizeof(DWORD);
	zt_int_t retval=ZT_OK;
	//size_t curpos=0;
	
	__try{
		req->hdr->use=0;
		req->response->use=0;
		if (HttpSendRequest(req->req,NULL,0,(LPVOID)data,siz)){
			//get the size headers
			HttpQueryInfo(req->req, HTTP_QUERY_RAW_HEADERS_CRLF, NULL, &d, &d2);
			
			if(d<req->hdr->siz){				
				HttpQueryInfo(req->req, HTTP_QUERY_RAW_HEADERS_CRLF,req->hdr->str,&d,&d2);
				req->hdr->use=d;//HTTP_QUERY_RAW_HEADERS_CRLF				
			}			
			HttpQueryInfo(req->req,HTTP_QUERY_STATUS_CODE | HTTP_QUERY_FLAG_NUMBER,&req->StatusCode,&dwlength,NULL);
			HttpQueryInfo(req->req,HTTP_QUERY_CONTENT_LENGTH | HTTP_QUERY_FLAG_NUMBER,&req->ContentLength,&dwlength,NULL);
			
			while(InternetReadFile(req->req,req->response->str+req->response->use,ZT_HTTP_IO_BUF_SIZ,&d2)){				
				if(d2<=0){
					__leave;
				}
				if(d2){
					req->response->use+=d2;
					if((req->response->use+ZT_HTTP_IO_BUF_SIZ)>req->response->siz){
						__leave;
					}					
				}
			}
			__leave;
		}
		else{		
			req->err=GetLastError();__leave;
		}		
	}
	__except(EXCEPTION_EXECUTE_HANDLER){
		retval=ZT_ERR;
	}
	ZT_STR_END(req->hdr);
	ZT_STR_END(req->response);

	return(ZT_OK);
}

ZT_API zt_int_t	zt_http_sync_request_close(HttpSyncRequest *req)
{
	if(!req){
		return(ZT_OK);
	}
	req->hdr->use=0;
	req->response->use=0;

	ZT_STR_END(req->hdr);
	ZT_STR_END(req->response);

	req->auto_redirect=0;	
	req->StatusCode=0;
	req->ContentLength=0;
	
	req->err=ZT_OK;
	/*
	if(req->req!=NULL){
		InternetCloseHandle(req->req);req->req=NULL;
	}
	if(req->conn!=NULL){
		InternetCloseHandle(req->conn);req->conn=NULL;
	}
	*/
	return(ZT_OK);
}

ZT_API zt_int_t	zt_http_sync_request_free(HttpSyncRequest *req)
{
	if(!req){
		return(ZT_OK);
	}
	
	if(req->req!=NULL){
		InternetCloseHandle(req->req);
		req->req=NULL;
	}
	if(req->conn!=NULL){
		InternetCloseHandle(req->conn);
		req->conn=NULL;
	}
	if(req->obj!=NULL){
		InternetCloseHandle(req->obj);
		req->obj=NULL;
	}
	free(req);
	req=NULL;

	return(ZT_OK);
}

ZT_API size_t zt_http_sync_request_get(HttpSyncRequest *req,u_char *uri,u_char *hdr,zt_str_t *tmp)
{
	//size_t siz=0;
	
	req->response->use=0;
	req->StatusCode=0;
	
	if(zt_http_sync_request_open(req,(u_char *)"GET",uri)==ZT_OK){
		zt_http_sync_request_header(req,(u_char *)"Accept-Encoding: gzip, deflate");
		if(hdr){
			zt_http_sync_request_header(req,hdr);
		}
		zt_http_sync_request_send(req,NULL,0);
		if(req->response->use>0){
			if(ZT_HTTP_SYNC_GZIP(req->hdr->str)){
				ZT_STR_MEMCPY(tmp,req->response);ZT_STR_END(tmp);
				zt_ungzip(req->response,tmp);
				ZT_STR_END(req->response);
			}
		}
	}
	
	ZT_STR_END(req->response);
	return(req->response->use);
}

ZT_API size_t zt_http_sync_request_post(HttpSyncRequest *req,u_char *uri,u_char *hdr,zt_str_t *data,zt_str_t *tmp)
{
	//size_t siz=0;
	req->response->use=0;
	req->StatusCode=0;
	
	if(zt_http_sync_request_open(req,(u_char *)"POST",uri)==ZT_OK){
		zt_http_sync_request_header(req,(u_char *)"Accept-Encoding: gzip, deflate\r\nContent-Type: application/x-www-form-urlencoded");
		if(hdr){
			zt_http_sync_request_header(req,hdr);
		}
		zt_http_sync_request_send(req,data->str,data->use);
		if(req->response->use>0){
			if(ZT_HTTP_SYNC_GZIP(req->hdr->str)){
				ZT_STR_MEMCPY(tmp,req->response);ZT_STR_END(tmp);
				zt_ungzip(req->response,tmp);
				ZT_STR_END(req->response);
			}
		}
	}
	
	ZT_STR_END(req->response);
	return(req->response->use);
}

ZT_API size_t zt_http_get(HttpSyncRequest *req,u_char *uri,u_char *hdr,zt_str_t *tmp)
{
	//u_long size=0;
		
	req->response->use=0;
	req->StatusCode=0;
	
	if(zt_http_sync_request_open(req,(u_char *)"GET",uri)==ZT_OK){
		zt_http_sync_request_header(req,(u_char *)"Accept-Encoding: gzip, deflate");
		if(hdr){
			zt_http_sync_request_header(req,hdr);
		}
		zt_http_sync_request_send(req,NULL,0);
		if(req->response->use>0){
			if(ZT_HTTP_SYNC_GZIP(req->hdr->str)){
				ZT_STR_MEMCPY(tmp,req->response);ZT_STR_END(tmp);
				req->response->use=zt_ungzip(req->response,tmp);
				ZT_STR_END(req->response);
			}
		}
	}
	
	ZT_STR_END(req->response);
	return(req->response->use);
}

ZT_API size_t zt_http_post(HttpSyncRequest *req,u_char *uri,u_char *hdr,zt_str_t *data,zt_str_t *tmp)
{
	//u_long size=0;
	
	req->response->use=0;
	req->StatusCode=0;
	
	if(zt_http_sync_request_open(req,(u_char *)"POST",uri)==ZT_OK){
		zt_http_sync_request_header(req,(u_char *)"Accept-Encoding: gzip, deflate\r\nContent-Type: application/x-www-form-urlencoded");
		if(hdr){
			zt_http_sync_request_header(req,hdr);
		}
		zt_http_sync_request_send(req,data->str,data->use);
		if(req->response->use>0){
			if(ZT_HTTP_SYNC_GZIP(req->hdr->str)){
				ZT_STR_MEMCPY(tmp,req->response);ZT_STR_END(tmp);
				req->response->use=zt_ungzip(req->response,tmp);
				ZT_STR_END(req->response);
			}
		}
	}
	
	ZT_STR_END(req->response);
	return(req->response->use);
}